/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2015.07.10                                          *
 * Description:      Region that consists of several elementary regions  *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/regn/Region.h"

//COMMON headers
#include "COMMON/info/InfoManager.h"
#include "COMMON/info/CommonInfo.h"
#include "COMMON/info/MeshInfo.h"
#include "COMMON/info/UnstructuredMeshInfo.h"
#include "COMMON/regn/SimpleRegion.h"
#include "COMMON/tbox/LogMessage.h"
#include "COMMON/tbox/Utilities.h"


namespace FreeMAPs
{

Region::Region(
    const std::string& parent_name,
    const std::string& region_name):
    Parent (parent_name, region_name, false),
    d_use_structured_mesh (false)
{}



Region::~Region()
{
    this->clear();
}



void
Region::clear()
{
    Utilities::clear_pointer_object (d_simple_regions);
    d_simple_region_names.clear();
}



void
Region::check_consistence() const
{
    /*
     * first check d_use_structured_mesh
     */
    const MeshInfo* mesh_info = InfoManager::get_instance()->get_mesh_info();
    {
        if (d_use_structured_mesh && !mesh_info->use_structured_mesh())
        {
            LogMessage::unknown_error (
                this->get_database(),
                "use_structured_mesh",
                "when set use_structured_mesh to TRUE, the structured mesh MUST be used in mesh_info");
        }

        if (!d_use_structured_mesh && !mesh_info->use_unstructured_mesh())
        {
            LogMessage::unknown_error (
                this->get_database(),
                "use_structured_mesh",
                "when set use_structured_mesh to FALSE, the unstructured mesh MUST be used in mesh_info");
        }

        this->finish_checking_consistence ("use_structured_mesh");
    }

    this->finish_checking_consistence ("simple_region_names");
}



bool
Region::check_all_info_db (
        const InputDatabase* db)
{
    //use_structured_mesh
    std::string region_name = db->get_name();
    if( !db->key_exists("use_structured_mesh") )
    {
        FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\", can not find  \"use_structured_mesh\" \n");
        return false;
    }
    if( !db->is_bool("use_structured_mesh") )
    {
        FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\", \"use_structured_mesh\" should be bool \n");
        return false;
    }

    //simple_region_names
    if( !db->key_exists("simple_region_names") )
    {
        FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\", can not find  \"simple_region_names\" \n");
        return false;
    }
    if( !db->is_string("simple_region_names") )
    {
        FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\", \"simple_region_names\" should be string \n");
        return false;
    }

    const std::vector<std::string> simple_region_names = db->get_string_vector ("simple_region_names");
    for (std::size_t i = 0; i < simple_region_names.size(); ++i)
    {
        const std::string& simple_region_name = simple_region_names[i];
        if( db->key_exists(simple_region_name) )
        {
            if( !db->is_database(simple_region_name) )
            {
                FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\", \"" + simple_region_name + "\", should be database, like this--> A \n{\n ......\n} \n");
                return false;
            }
//            _body
//             {
//                  attribute_name = "attr_Al"
//             }
            const InputDatabase* db_simple_region = db->get_database(simple_region_name);
            //attribute_name
            if( !db_simple_region->key_exists("attribute_name") )
            {
                FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\", in \"" + simple_region_name + "\", can not find  \"attribute_name\" \n");
                return false;
            }
            if( !db_simple_region->is_string("attribute_name") )
            {
                FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\",  in \"" + simple_region_name + "\", \"attribute_name\" should be string \n");
                return false;
            }
//            //attribute_name exist in attribute_info or not
//            std::string attr_name = db_simple_region->get_string("attribute_name");
//            const InputDatabase* db_region = db->get_parent_database();
//            const InputDatabase* db_region_info = db_region->get_parent_database();
//            const InputDatabase* db_attribute_info = db_region_info->get_database("attribute_info");

//            if( !db_attribute_info->key_exists(attr_name) )
//            {
//                FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\", in \"" + simple_region_name + ", attribute_name = " + attr_name + "\" but in \"attribute_info\" dose not exist! \n");
//                return false;
//            }
        }
        else
        {
            FREEMAPS_ERROR("in \"region_info\", in \"" + region_name + "\", can not find \"" + simple_region_name + "\" but in \"simple_region_names\" exist \n");
            return false;
        }
    }

    return true;
}



void
Region::get_from_input (
        const InputDatabase* db)
{
    this->prepare_for_use (db);

    db->insert_allowable_key ("use_structured_mesh");
    db->insert_allowable_key_with_items ("simple_region_names");

    db->check_allowable_keys();

    /*
     * build use_structured_mesh
     */
    {
        if(db->key_exists("use_structured_mesh") && db->is_bool("use_structured_mesh"))
        {
            db->check_requisite_key ("use_structured_mesh", DataType::BOOL, false, false);
            d_use_structured_mesh = db->get_bool ("use_structured_mesh");

            this->add_key_for_checking_consistence ("use_structured_mesh", "check if mesh type is rational");
        }
    }

    /*
     * build simple_region_names
     */
    {
        if(db->key_exists("simple_region_names") && db->is_string("simple_region_names"))
        {
            db->check_requisite_key ("simple_region_names", DataType::STRING, false, true);
            db->check_duplicated_string_value ("simple_region_names");

            const std::vector<std::string> simple_region_names = db->get_string_vector ("simple_region_names");

            for (std::size_t i = 0; i < simple_region_names.size(); ++i)
            {
                const std::string& simple_region_name = simple_region_names[i];

                //exist && is database
                if(db->key_exists(simple_region_name) && db->is_database(simple_region_name))
                {
                    db->check_requisite_key (simple_region_name, DataType::DATABASE, false, false);

                    SimpleRegion* simple_region = new SimpleRegion (this->get_full_name(), simple_region_name, d_use_structured_mesh);

                    simple_region->get_from_input (db->get_database (simple_region_name));
                    d_simple_regions.insert (std::make_pair (simple_region_name, simple_region));

                    d_simple_region_names.insert(simple_region_name);
                }
            }

            this->add_key_for_checking_consistence ("simple_region_names", "check if it is a exist subdomain names");
        }
    }
}



std::ostream&
operator << (
    std::ostream& os,
    const Region& rhs)
{
    os << rhs.get_region_name() << "\n{\n";

    os << LogMessage::get_prefix (ONE_TAB) << "use_structured_mesh = ";
    os << LogMessage::get_bool_string (rhs.use_structured_mesh()) << "\n";

    os << LogMessage::get_log_info_from_map_keys (
           "simple_region_names",
           rhs.d_simple_regions,
           ONE_TAB,
           true) << '\n';

    for (std::map<std::string, SimpleRegion*>::const_iterator pos = rhs.d_simple_regions.begin();
            pos != rhs.d_simple_regions.end(); ++pos)
    {
        std::ostringstream oss;
        oss << *pos->second;
        os << '\n' << LogMessage::get_log_info (oss, ONE_TAB) << '\n';
    }

    os << "}";

    return os;
}



void
Region::add_simple_region(
        const std::string simple_region_name,
        const std::string attr_name)
{
    SimpleRegion* simple_region = new SimpleRegion (this->get_full_name(), simple_region_name, d_use_structured_mesh);
    simple_region->set_attribute(attr_name);
    d_simple_regions.insert (std::make_pair (simple_region_name, simple_region));
}

} //namespace FreeMAPs
